# -*- coding: utf-8 -*-
#
# Author:: Jonny
# Date:: 2022/4/24
import os
import time
import traceback
import uuid

import cv2

from compat.project_paths import project_tmp_images_path

from submodules.common.lib.datetime_ import get_timestamp
from submodules.common.lib.image_ import reduce_noise_file

from submodules.common.lib.log_ import get_logger

logger = get_logger()

from PIL import Image
import numpy as np


def img_cut(image_path_in, bounds_tuple, image_path_out_suffix="", image_path_out=None):
    """

    :param filename_in: "img/current.png"
    :param filename_out: "img/current_cut.png"
    :param _tuple: (396, 1601, 666, 1668)   (left, upper, right, lower)  左上右下
    :return:
    """
    # for i in range(30):
    #     if os.path.exists(image_path_in):
    #         break
    #     else:
    #         pass
    #     sleep(0.1)

    if image_path_out == None:
        image_path_out = os.path.join(
            project_tmp_images_path,
            get_timestamp()
            + "-"
            + image_path_out_suffix
            + "-"
            + str(uuid.uuid1())
            + ".png",
        )
    logger.info("image_path_out:%s" % image_path_out)

    img = Image.open(image_path_in)
    cropped = img.crop(bounds_tuple)
    cropped.save(image_path_out)
    return image_path_out


def _hamming_distance(a, b):
    return bin(a ^ b).count("1")


from functools import reduce


def _phash(img):
    # img = img.resize((20, 20), Image.ANTIALIAS).convert('L')
    # avg = reduce(lambda x, y: x + y, img.getdata()) / 400.

    img = img.resize((40, 40), Image.ANTIALIAS).convert("L")
    avg = reduce(lambda x, y: x + y, img.getdata()) / 1600.0

    return reduce(
        lambda x, y: x | (y[1] << y[0]),
        enumerate(map(lambda i: 0 if i < avg else 1, img.getdata())),
        0,
    )


def _dHash(img):
    # 差值哈希算法
    # 缩放8*8
    img = cv2.resize(img, (9, 8))
    # 转换灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    hash_str = ""
    # 每行前一个像素大于后一个像素为1，相反为0，生成哈希
    for i in range(8):
        for j in range(8):
            if gray[i, j] > gray[i, j + 1]:
                hash_str = hash_str + "1"
            else:
                hash_str = hash_str + "0"
    return hash_str


def cv2_imgread(filename):
    try:
        img = cv2.imdecode(np.fromfile(filename, dtype=np.uint8), cv2.IMREAD_COLOR)
        return img
    except Exception as e:
        logger.error(repr(e))
        logger.error(traceback.format_exc())
        return None


def is_image_match(
    img_path1, img_path2, threshold=0.9, bounds=None, is_reduce_noise=False
):
    logger.info(f"img_path1:{img_path1}")
    logger.info(f"img_path2:{img_path2}")

    if bounds is not None:
        img_path2 = img_cut(img_path2, bounds)
        logger.info(f"img2_cut_path:{img_path2}")

    # 哈希值法
    # # 降噪
    # img1 = reduce_noise(img1, threshold=150)
    # # img1.show()
    # img2 = reduce_noise(img2, threshold=150)
    # # img1.show()
    #
    # # hamming_distance = _hamming_distance(_dHash(img1), _dHash(img2))
    # hamming_distance = _hamming_distance(_phash(img1), _phash(img2))
    # logger.info(f"hamming_distance:{hamming_distance}")
    # logger.info(f"threshold:{threshold}")
    # if hamming_distance <= threshold:
    #     return True
    # else:
    #     return False

    # 结构相似性算法
    # img1 = cv2_imgread(img_path1)
    # img2 = cv2_imgread(img_path2)
    # ssim_score = get_ssim_score(img1, img2)
    # logger.info(f"相似度为：{ssim_score}")
    # if ssim_score > threshold:
    #     return True
    # else:
    #     return False

    # 轮廓（暂时用哈希）+直方图
    img1 = cv2_imgread(img_path1)
    img2 = cv2_imgread(img_path2)
    hist_score = classify_hist_with_split(img1, img2)
    logger.info(f"hist_score:{hist_score}")
    if is_reduce_noise:
        img_path1 = reduce_noise_file(img_path1, img_path_out=None, threshold=100)
        img_path2 = reduce_noise_file(img_path2, img_path_out=None, threshold=100)
    if hist_score > 0.7:
        # 降噪
        """
        对于细体子150太大了，降噪后成了空白图片
        对于对比度过低的图片阈值不好设置
        """

        # 哈希
        # img1 = Image.open(img_path1)
        # img2 = Image.open(img_path2)
        # hamming_distance = _hamming_distance(_phash(img1), _phash(img2))
        # img1.close()
        # img2.close()
        # logger.info(f"hamming_distance:{hamming_distance}")
        # if hamming_distance < 150:
        #     ret = True
        #     logger.info(f"is_image_match:{ret}")
        #     return ret
        # else:
        #     ret = False
        #     logger.info(f"is_image_match:{ret}")
        #     return ret

        # 哈希算法
        img1 = cv2_imgread(img_path1)
        img2 = cv2_imgread(img_path2)
        phash_socre = img_compare_by_hash(img1, img2, mode=1)
        logger.info(f"phash_socre:{phash_socre}")
        if phash_socre > threshold:
            ret = True
            logger.info(f"is_image_match:{ret}")
            return ret
        else:
            ret = False
            logger.info(f"is_image_match:{ret}")
            return ret

        # 结构相似性算法
        # img1 = cv2_imgread(img_path1)
        # img2 = cv2_imgread(img_path2)
        # ssim_score = get_ssim_score(img1, img2)
        # logger.info(f"相似度为：{ssim_score}")
        # if ssim_score > 0.9:
        #     return True
        # else:
        #     return False

    else:
        ret = False
        logger.info(f"is_image_match:{ret}")
        return ret


def is_image_match_old(img_path1, img_path2, threshold=50, bounds=None):
    # hamming
    img1 = Image.open(img_path1)
    logger.info(f"img_path1:{img_path1}")
    logger.info(f"img_path2:{img_path2}")

    if bounds == None:
        img2 = Image.open(img_path2)
    else:
        img2_cut_path = img_cut(img_path2, bounds)
        logger.info(f"img2_cut_path:{img2_cut_path}")

        img2 = Image.open(img2_cut_path)
    hamming_distance = _hamming_distance(_phash(img1), _phash(img2))
    logger.info(f"hamming_distance:{hamming_distance}")
    logger.info(f"threshold:{threshold}")
    if hamming_distance <= threshold:
        return True
    else:
        return False


def calculate(image1, image2):
    # 灰度直方图算法
    # 计算单通道的直方图的相似值
    hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
    hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
    # 计算直方图的重合度
    degree = 0
    for i in range(len(hist1)):
        if hist1[i] != hist2[i]:
            degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
        else:
            degree = degree + 1
    degree = degree / len(hist1)
    return degree


def classify_hist_with_split(image1, image2, size=(256, 256)):
    """
    问道中同区域明暗图片相似度仅0.33
    :param image1:
    :param image2:
    :param size:
    :return:
    """
    # RGB每个通道的直方图相似度
    # 将图像resize后，分离为RGB三个通道，再计算每个通道的相似值
    image1 = cv2.resize(image1, size)
    image2 = cv2.resize(image2, size)
    sub_image1 = cv2.split(image1)
    sub_image2 = cv2.split(image2)
    sub_data = 0
    for im1, im2 in zip(sub_image1, sub_image2):
        sub_data += calculate(im1, im2)
    sub_data = sub_data / 3
    return sub_data


from skimage.metrics import structural_similarity


def get_ssim_score(img1, img2):
    """
    问道中同区域明暗图片相似度为0.79
    :param img1:
    :param img2:
    :return:
    """
    try:
        # img1 = cv2.imread('1.png')
        # img2 = cv2.imread('2.png')
        img2 = cv2.resize(img2, (img1.shape[1], img1.shape[0]))
        ssim_score = structural_similarity(
            img1, img2, data_range=255, multichannel=True
        )
        return ssim_score
    except Exception as e:
        logger.error(repr(e))
        logger.error(traceback.format_exc())
        return 0


# 感知哈希算法
def pHash(image):
    # image = cv2.resize(image, (32, 32), interpolation=cv2.INTER_CUBIC)
    image = cv2.resize(image, (64, 64), interpolation=cv2.INTER_CUBIC)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    #     cv2.imshow('image', image)
    #     cv2.waitKey(0)
    #     cv2.destroyAllWindows()
    # 将灰度图转为浮点型，再进行dct变换
    dct = cv2.dct(np.float32(image))
    #     print(dct)
    # 取左上角的8*8，这些代表图片的最低频率
    # 这个操作等价于c++中利用opencv实现的掩码操作
    # 在python中进行掩码操作，可以直接这样取出图像矩阵的某一部分
    dct_roi = dct[0:8, 0:8]
    avreage = np.mean(dct_roi)
    hash = []
    for i in range(dct_roi.shape[0]):
        for j in range(dct_roi.shape[1]):
            if dct_roi[i, j] > avreage:
                hash.append(1)
            else:
                hash.append(0)
    return hash


# 均值哈希算法
def aHash(image):
    # 缩放为8*8
    image = cv2.resize(image, (8, 8), interpolation=cv2.INTER_CUBIC)
    # 转换为灰度图
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    avreage = np.mean(image)
    hash = []
    for i in range(image.shape[0]):
        for j in range(image.shape[1]):
            if image[i, j] > avreage:
                hash.append(1)
            else:
                hash.append(0)
    return hash


# 差值感知算法
def dHash(image):
    # 缩放9*8
    image = cv2.resize(image, (9, 8), interpolation=cv2.INTER_CUBIC)
    # 转换灰度图
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    #     print(image.shape)
    hash = []
    # 每行前一个像素大于后一个像素为1，相反为0，生成哈希
    for i in range(8):
        for j in range(8):
            if image[i, j] > image[i, j + 1]:
                hash.append(1)
            else:
                hash.append(0)
    return hash


# 计算汉明距离
def Hamming_distance(hash1, hash2):
    num = 0
    for index in range(len(hash1)):
        if hash1[index] != hash2[index]:
            num += 1
    return num


def img_compare_by_hash(img1, img2, mode=0):
    # image_file1 = './data/cartoon1.jpg'
    # image_file2 = './data/cartoon3.jpg'
    # img1 = cv2.imread(image_file1)
    # img2 = cv2.imread(image_file2)
    if mode == 0:
        hash1 = pHash(img1)
        hash2 = pHash(img2)
    elif mode == 1:
        hash1 = dHash(img1)
        hash2 = dHash(img2)
    elif mode == 2:
        hash1 = aHash(img1)
        hash2 = aHash(img2)
    dist = Hamming_distance(hash1, hash2)
    # 将距离转化为相似度
    similarity = 1 - dist * 1.0 / 64
    return similarity


if __name__ == "__main__":

    def __test_img_cut():
        from lib.adb_ import get_screenshot

        screenshot_path = get_screenshot()

        # 刷到对话框边界 (772,28,1256,695)
        # 老君查岗弹窗-验证码 (208,119,1071,649)
        res = img_cut(screenshot_path, (772, 28, 1256, 695))
        print(res)

    def __test_is_image_match():
        from obj import obj_bounds

        t1 = time.time()
        img_path1 = "D:/20230101132039_2.png"
        img_path2 = "D:/20230101132039_2.png"

        # res = is_image_match(img_path1=img_path1, img_path2=img_path2)
        # print(res)
        # t2 = time.time()
        # print(t2 - t1)

        # img_path1 = "D:/light.png"
        # img_path2 = "D:/dark.png"
        # img_path1 = img_cut(img_path1, (1177, 119, 1260, 150))
        # img_path2 = img_cut(img_path2, (1177, 119, 1260, 150))
        #
        # img1 = cv2.imread(img_path1)
        # img2 = cv2.imread(img_path2)
        # res = classify_hist_with_split(img1, img2)
        # print(res)

        # ssim算法
        img_path1 = "D:/light.png"
        img_path2 = "D:/dark.png"
        img_path1 = img_cut(img_path1, (1177, 119, 1260, 150))
        img_path2 = img_cut(img_path2, (1177, 119, 1260, 150))
        # img_path2 = img_path1

        t1 = time.time()
        res = is_image_match(img_path1, img_path2)
        t2 = time.time()
        print(t2 - t1)
        print(res)

    def __test_get_ssim_score():
        img_path1 = "D:/light.png"
        img_path2 = "D:/dark.png"
        img_path1 = img_cut(img_path1, (1177, 119, 1260, 150))
        img_path2 = img_cut(img_path2, (1177, 119, 1260, 150))
        img1 = cv2.imread(img_path1)
        img2 = cv2.imread(img_path2)
        res = get_ssim_score(img1, img2)
        print(res)

    __test_is_image_match()
